// <copyright file="CallScriptInstruction.cs" company="Codeplex project Eco.Contrib"> 
// The MIT License (MIT) 
// </copyright> 
// <author>Peter Buchmann</author> 

namespace Eco.Contrib
{
    using System;
    using System.Collections.Generic;
    using Eco.ObjectRepresentation;
    using Eco.Services;
    using Eco.UmlCodeAttributes;

    /// <summary>
    /// CallScriptInstruction: Instruction that represents a call to a other instruction
    /// </summary>
    public partial class CallScriptInstruction
    {
        /// <summary>
        /// Executes the function.
        /// </summary>
        /// <param name="serviceProvider">The service provider.</param>
        /// <param name="variableList">The variable list.</param>
        /// <returns>Return value of executed function</returns>
        [UmlElement(Id = "98e6d420-baea-4e5c-a13c-a3dc3c2702c8")]
        protected internal override IElement ExecuteFunction(IEcoServiceProvider serviceProvider, IModifiableVariableList variableList)
        {
            EcoScript ecoScript = this.EcoScript;
            if (ecoScript == null)
            {
                ecoScript = this.ServiceProvider().GetEcoService<IOclService>().Evaluate("EcoScript.AllInstances->select(Name='" + this.ScriptName + "')->first").GetValue<EcoScript>();
            }

            if (ecoScript == null)
            {
                throw new EcoContribException("Was not able to find script '" + this.ScriptName + "'");
            }

            if (serviceProvider == null)
            {
                throw new EslException("Parameter is null");
            }

            IModifiableVariableList subVariableList = serviceProvider.GetEcoService<IVariableFactoryService>().CreateVariableList();
            int parameterLoop = 0;
            foreach (InstructionWithOcl oclInstruction in this.OCLParameter)
            {
                try
                {
                    IElement parameter = oclInstruction.ExecuteFunction(serviceProvider, variableList);
                    if (parameter != null)
                    {
                        ecoScript.AddParameter(subVariableList, parameter);
                    }
                }
                catch (Exception ex)
                {
                    throw new EcoContribException(string.Format(System.Globalization.CultureInfo.CurrentCulture,"Error during evaluation of parameter {0}, OCL: {1}.", parameterLoop, oclInstruction.OCL), ex);
                }

                parameterLoop++;
            }

            IElement result = null;
            try
            {
                result = ecoScript.Execute(serviceProvider, subVariableList);
            }
            catch (Exception ex)
            {
                if (ex is EslException)
                {
                    throw;
                }
                else
                {
                    throw new EslException(string.Format(System.Globalization.CultureInfo.CurrentCulture, "Error: {0}\n at Position ({1},{2}) of Script '{3}':\n{4}", ex.Message, ScriptPosition.Line, ScriptPosition.Col, Script.Name, Script.GetText(ScriptPosition.Pos - 1, 100)), ScriptPosition.Line, ScriptPosition.Col, ScriptPosition.Pos, ex);
                }
            }

            if (this.ResultVariable != null)
            {
                IElement ie1 = serviceProvider.GetEcoService<IVariableFactoryService>().NullReference;
                if (result != null && result.Element.AsObject != null)
                {
                    ie1 = result.Element;
                }

                this.ResultVariable.Set(variableList, ie1);
            }

            return serviceProvider.GetEcoService<IVariableFactoryService>().NullReference;
        }

        /// <summary>
        /// Inits the specified tokens.
        /// </summary>
        /// <param name="tokens">The tokens.</param>
        [UmlElement(Id = "cfd38d22-e122-4708-8eec-e570b3e20506")]
        protected internal override void Init(Dictionary<IList<EslToken>, Type> tokens)
        {
            if (tokens == null)
            {
                throw new EslException("Parameter is null");
            }
            
            List<EslToken> tokenList = new List<EslToken>();
            tokenList.Add(new EslToken(EslTokenType.Word, "Call"));
            tokenList.Add(new EslToken(EslTokenType.Word, "%"));
            tokens.Add(tokenList, this.GetType());
            tokenList = new List<EslToken>();
            tokenList.Add(new EslToken(EslTokenType.Word, "%"));
            tokenList.Add(new EslToken(EslTokenType.Operator, ":="));
            tokenList.Add(new EslToken(EslTokenType.Word, "Call"));
            tokenList.Add(new EslToken(EslTokenType.Word, "%"));
            tokens.Add(tokenList, this.GetType());
        }

        /// <summary>
        /// Parses the specified token list.
        /// </summary>
        /// <param name="tokenList">The token list.</param>
        /// <param name="ecoScript">The eco script.</param>
        /// <param name="fullExpression">if set to <c>true</c> [full expression].</param>
        [UmlElement(Id = "54b1238a-7947-487a-abc5-e9ed37966f54")]
        protected internal override void Parse(IList<EslToken> tokenList, EcoScript ecoScript, bool fullExpression)
        {
            if (tokenList == null || ecoScript == null)
            {
                throw new EslException("Parameter is null");
            }

            EslToken token = GetNextToken(tokenList);
            RememberEcoScript(ecoScript, token);
            EslToken secondToken = GetNextToken(tokenList);

            if ((token.TokenType == EslTokenType.Word) &&
                 (secondToken.TokenType == EslTokenType.Operator) && (secondToken.TokenValue == ":="))
            {
                this.ResultVariable = new Variable(this.ServiceProvider());
                this.ResultVariable.Name = token.TokenValue;
                token = GetNextToken(tokenList);
                secondToken = GetNextToken(tokenList);
            }

            // Read name of script
            this.ScriptName = secondToken.TokenValue;
            do
            {
                token = GetNextToken(tokenList);
                if ((token.TokenType == EslTokenType.Word) ||
                 ((token.TokenType == EslTokenType.Operator) && (token.TokenValue == ".")))
                {
                    this.ScriptName += token.TokenValue;
                }
            }
            while ((token.TokenType != EslTokenType.Operator) || (token.TokenValue != "("));

            token = GetNextToken(tokenList);
            if ((token.TokenType != EslTokenType.Operator) || (token.TokenValue != ")"))
            {
                tokenList.Insert(0, token);
                do
                {
                    EslInstruction instruction = ecoScript.ReadNextInstruction(tokenList, false);
                    InstructionWithOclAndVariable instructionWithOclAndVariable = instruction as InstructionWithOclAndVariable;
                    if (instructionWithOclAndVariable != null)
                    {
                        this.OCLParameter.Add(instructionWithOclAndVariable);
                    }
                    else
                    {
                        throw new EslException("variable := EAL|OCL|OCLPS(..) expected after " + token.ToString(), token);
                    }

                    token = GetNextToken(tokenList);
                }
                while ((token.TokenType != EslTokenType.Operator) || (token.TokenValue != ")"));
            }

            if (fullExpression)
            {
                EslToken tokenEnd = GetNextToken(tokenList);
                AssertInstructionEnd(tokenEnd);
            }
        }
    }
}